﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using depthcamsensor = Microsoft.Robotics.Services.DepthCamSensor;
using webcamsensor = Microsoft.Robotics.Services.WebCamSensor;
using Microsoft.Robotics.Services.Sensors.Kinect.Proxy;
using System.IO;

namespace KinectCamRecorder
{
    /// <summary>
    /// Interaction logic for KinectCamRecorderWindow.xaml
    /// </summary>
    public partial class KinectCamRecorderUI : Window
    {
        private List<webcamsensor.WebCamSensorState> _webCamFrameList = new List<webcamsensor.WebCamSensorState>();
        private List<depthcamsensor.DepthCamSensorState> _depthCamFrameList = new List<depthcamsensor.DepthCamSensorState>();

        private KinectCamRecorderOperations _mainPort;

        public KinectCamRecorderUI(KinectCamRecorderOperations mainPort)
        {
            _mainPort = mainPort;

            InitializeComponent();
        }

        public void AddWebCamFrame(webcamsensor.WebCamSensorState frameState)
        {
            if (frameState.Data != null) 
            {
                _webCamImage.Source = frameState.BitmapSource;
                
                if (_recordButton.IsChecked == true)
                {
                    webcamsensor.WebCamSensorState newState = new webcamsensor.WebCamSensorState();
                    newState.Data = (byte[])frameState.Data.Clone();
                    newState.DeviceName = frameState.DeviceName;
                    newState.TimeStamp = frameState.TimeStamp;
                    newState.Width = frameState.Width;
                    newState.Height = frameState.Height;
                    newState.Stride = frameState.Stride;
                    _webCamFrameList.Add(newState);

                    // collection items get modified internally?
                    //_webCamFrameList.Add(frameState);
                }
            }

            _recordedRGBFrames.Content = _webCamFrameList.Count.ToString();
        }

        public void AddDepthCamFrame(depthcamsensor.DepthCamSensorState frameState)
        {
            if (frameState.DepthImage != null)
            {
                byte[] data = ToByteArray(frameState.DepthImageSize.Width, frameState.DepthImageSize.Height, frameState.DepthImage);

                _depthCamImage.Source = BitmapSource.Create(
                    frameState.DepthImageSize.Width, 
                    frameState.DepthImageSize.Height, 
                    96, 96, 
                    PixelFormats.Bgr24,
                    null,
                    data,
                    frameState.DepthImageSize.Width * 3);

                if (_recordButton.IsChecked == true)
                {
                    _depthCamFrameList.Add(frameState);
                }
            }

            _recordedDepthFrames.Content = _depthCamFrameList.Count.ToString();
        }

        private byte[] ToByteArray(int width, int height, short[] depthData)
        {
            // NOTE: This code implicitly assumes that the width is a multiple
            // of four bytes because Bitmaps have to be longword aligned.
            byte[] buff = new byte[width * height * 3];
            byte val;
            int j = 0;
            for (int i = 0; i < width * height; i++)
            {
                // Convert the data to a suitable range
                if (depthData[i] >= (short)KinectReservedSampleValues.MaxDepthDataValue)
                {
                    val = byte.MaxValue;
                }
                else if (depthData[i] <= (short)KinectReservedSampleValues.MinValidDepth)
                {
                    val = byte.MaxValue;
                }
                else
                {
                    val = (byte)(((double)depthData[i] / (double)KinectReservedSampleValues.MaxDepthDataValue) * byte.MaxValue);
                }
                // Set all R, G and B values the same, i.e. gray scale
                buff[j++] = val;
                buff[j++] = val;
                buff[j++] = val;
            }

            return buff;
        }

        private void _recordButton_Checked(object sender, RoutedEventArgs e)
        {
            _recordButton.Content = "Stop";
        }

        private void _recordButton_Unchecked(object sender, RoutedEventArgs e)
        {
            _recordButton.Content = "Record";
        }

        private void _clearButton_Click(object sender, RoutedEventArgs e)
        {
            _depthCamFrameList.Clear();
            _webCamFrameList.Clear();

            _recordedDepthFrames.Content = "0";
            _recordedRGBFrames.Content = "0";
        }

        private void _saveButton_Click(object sender, RoutedEventArgs e)
        {
            _recordButton.IsChecked = false;
            _recordButton.Content = "Record";

            Microsoft.Win32.SaveFileDialog saveDialog = new Microsoft.Win32.SaveFileDialog();

            saveDialog.FileName = "KinectFootage_" + DateTime.Now.ToString("yyyyMMddHHmmss");
            saveDialog.DefaultExt = "rec";
            saveDialog.Filter = "rec files (*.rec)|*.rec";
           
            Nullable<bool> result = saveDialog.ShowDialog();

            if (result == true && _webCamFrameList.Count > 0)
            {
                FileStream fs = new FileStream(saveDialog.FileName, FileMode.CreateNew);
                BinaryWriter writer = new BinaryWriter(fs);

                writer.Write(_depthCamFrameList.Count);

                
                foreach (var depthFrame in _depthCamFrameList)
                {
                    depthFrame.Serialize(writer);
                }

                writer.Write(_webCamFrameList.Count);


                foreach (var webCamFrame in _webCamFrameList)
                {
                    webCamFrame.Serialize(writer);
                }

                writer.Flush();
                writer.Close();
                        
                fs.Close();
            }
        }
    }
}